/**
*		@file ProcTable.cpp
*		@brief ProcTable class
*
*		@author Lan Ziquan
*
*		@date 1/25/2012
*/
#include "ProcTable.h"

vector<ProcEntry*>	ProcTable::vp_proc_entries_;
vector<ProcSet>		ProcTable::ProcPath_table_;

// step1: check existence
// step2: add proc
ProcIdx ProcTable::InsertProc(ProcName proc_name)
{
	// check existence
	ProcEntry* p_proc_entry_temp = NULL;
	if( CheckProcNameExistence(proc_name, &p_proc_entry_temp) )
		return p_proc_entry_temp->proc_idx;

	// add proc
	p_proc_entry_temp = new ProcEntry;
	p_proc_entry_temp->proc_idx = vp_proc_entries_.size()+1;
	p_proc_entry_temp->proc_name = proc_name;
	p_proc_entry_temp->stmt_num_start = p_proc_entry_temp->stmt_num_end = 0;
	p_proc_entry_temp->p_tnode_AST_root = NULL;
	p_proc_entry_temp->p_gnode_CFG_root = NULL;
	p_proc_entry_temp->proc_hierarchy = 0;


	vp_proc_entries_.push_back(p_proc_entry_temp);
	return p_proc_entry_temp->proc_idx;
}

ProcIdx ProcTable::InsertCallee(ProcName proc_caller_name, ProcName proc_callee_name)
{
	// check existence
	ProcEntry* p_proc_entry_caller = NULL;
	CheckProcNameExistence(proc_caller_name, &p_proc_entry_caller);

	ProcEntry* p_proc_entry_callee = NULL;
	if( CheckProcNameExistence(proc_callee_name, &p_proc_entry_callee) )
	{
		p_proc_entry_caller->set_callees.insert(p_proc_entry_callee->proc_idx);
		p_proc_entry_callee->set_callers.insert(p_proc_entry_caller->proc_idx);
		if( p_proc_entry_caller->proc_hierarchy >= p_proc_entry_callee->proc_hierarchy )
		{
			queue<ProcIdx> q_temp;
			p_proc_entry_callee->proc_hierarchy = p_proc_entry_caller->proc_hierarchy+1;
			q_temp.push(p_proc_entry_callee->proc_idx);

			while( !q_temp.empty() )
			{
				ProcIdx temp_idx = q_temp.front();
				q_temp.pop();

				set<ProcIdx>::iterator temp_iter;
				for( temp_iter = GetProcEntry(temp_idx)->set_callees.begin();
					temp_iter != GetProcEntry(temp_idx)->set_callees.end();
					temp_iter++)
				{
					if( GetProcEntry(*temp_iter)->proc_hierarchy <= GetProcEntry(temp_idx)->proc_hierarchy )
					{
						GetProcEntry(*temp_iter)->proc_hierarchy = GetProcEntry(temp_idx)->proc_hierarchy+1;
						q_temp.push(*temp_iter);
					}
				}
			}
		}
		return p_proc_entry_callee->proc_idx;
	}
	else
	{
		p_proc_entry_callee = new ProcEntry;
		p_proc_entry_callee->proc_idx = vp_proc_entries_.size()+1;
		p_proc_entry_callee->proc_name = proc_callee_name;
		p_proc_entry_callee->stmt_num_start = p_proc_entry_callee->stmt_num_end = 0;
		p_proc_entry_callee->p_tnode_AST_root = NULL;
		p_proc_entry_callee->p_gnode_CFG_root = NULL;
		p_proc_entry_callee->proc_hierarchy = p_proc_entry_caller->proc_hierarchy+1;
		vp_proc_entries_.push_back(p_proc_entry_callee);

		p_proc_entry_callee->set_callers.insert(p_proc_entry_caller->proc_idx);
		p_proc_entry_caller->set_callees.insert(p_proc_entry_callee->proc_idx);

		return p_proc_entry_callee->proc_idx;
	}
}

void ProcTable::SetStmtNumRange(ProcIdx proc_idx, StmtNum stmt_num_start, StmtNum stmt_num_end)
{
	ProcEntry* p_proc_entry = GetProcEntry(proc_idx);
	p_proc_entry->stmt_num_start = stmt_num_start;
	p_proc_entry->stmt_num_end = stmt_num_end;
	return;
}

void ProcTable::SetProcASTRoot(ProcIdx proc_idx, TNode* p_tnode_root)
{
	GetProcEntry(proc_idx)->p_tnode_AST_root = p_tnode_root;
	return;
}

void ProcTable::SetProcCFGRoot(ProcIdx proc_idx, GNode* p_gnode_root)
{
	GetProcEntry(proc_idx)->p_gnode_CFG_root = p_gnode_root;
	return;
}

ProcIdx ProcTable::GetProcIdx(ProcName proc_name)
{
	ProcEntry* p_proc_entry_temp = NULL;
	if( CheckProcNameExistence(proc_name, &p_proc_entry_temp) )
		return p_proc_entry_temp->proc_idx;
	return -1;
}

ProcName ProcTable::GetProcName(ProcIdx proc_idx)
{
	return GetProcEntry(proc_idx)->proc_name;
}

TNode* ProcTable::GetProcASTRoot(ProcIdx proc_idx)
{
	return GetProcEntry(proc_idx)->p_tnode_AST_root;
}

GNode* ProcTable::GetProcCFGRoot(ProcIdx proc_idx)
{
	return GetProcEntry(proc_idx)->p_gnode_CFG_root;
}

StmtNum ProcTable::GetStmtNumStart(ProcIdx proc_idx)
{
	return GetProcEntry(proc_idx)->stmt_num_start;
}

StmtNum ProcTable::GetStmtNumEnd(ProcIdx proc_idx)
{
	return GetProcEntry(proc_idx)->stmt_num_end;
}

queue<ProcIdx> ProcTable::GetProcSequence()
{
	list<ProcEntry*> lp_proc_entry;

	vector<ProcEntry*>::iterator iter_p_proc_entry_new;
	for( iter_p_proc_entry_new = vp_proc_entries_.begin();
		iter_p_proc_entry_new != vp_proc_entries_.end();
		iter_p_proc_entry_new++ )
	{
		list<ProcEntry*>::iterator iter_p_proc_entry_old;
		bool b_inserted = false;
		for( iter_p_proc_entry_old = lp_proc_entry.begin();
			iter_p_proc_entry_old != lp_proc_entry.end();
			iter_p_proc_entry_old++ )
		{
			if( (*iter_p_proc_entry_new)->proc_hierarchy >= (*iter_p_proc_entry_old)->proc_hierarchy )
			{
				lp_proc_entry.insert(iter_p_proc_entry_old, (*iter_p_proc_entry_new));
				b_inserted = true;
				break;
			}
		}
		if( !b_inserted ) lp_proc_entry.push_back(*iter_p_proc_entry_new);
	}

	queue<ProcIdx> q_proc_idx_result;
	list<ProcEntry*>::iterator iter_p_proc_entry_result;
	for( iter_p_proc_entry_result = lp_proc_entry.begin();
		iter_p_proc_entry_result != lp_proc_entry.end();
		iter_p_proc_entry_result++ )
		q_proc_idx_result.push((*iter_p_proc_entry_result)->proc_idx);

	return q_proc_idx_result;
}

ProcSet ProcTable::GetProcCallers(ProcIdx proc_idx)
{
	ProcSet proc_set_result (GetSize(), false);

	set<ProcIdx>::iterator iter_proc_idx;
	for( iter_proc_idx = GetProcEntry(proc_idx)->set_callers.begin();
		iter_proc_idx != GetProcEntry(proc_idx)->set_callers.end();
		iter_proc_idx++ )
		proc_set_result[(*iter_proc_idx)-1] = true;

	return proc_set_result;
}

ProcSet ProcTable::GetProcCallees(ProcIdx proc_idx)
{
	ProcSet proc_set_result (GetSize(), false);

	set<ProcIdx>::iterator iter_lroc_idx;
	for( iter_lroc_idx = GetProcEntry(proc_idx)->set_callees.begin();
		iter_lroc_idx != GetProcEntry(proc_idx)->set_callees.end();
		iter_lroc_idx++ )
		proc_set_result[(*iter_lroc_idx)-1] = true;

	return proc_set_result;
}

ProcIdx ProcTable::GetUndefinedProcIdx()
{
	for( int i = 0; i < ProcTable::GetSize(); i++ )
	{
		if( ProcTable::GetProcASTRoot(i+1) == NULL )
			return i+1;
	}
	return 0;
}

string ProcTable::ToString()
{
	stringstream ss_result;
	ss_result << "ProcTable :\n";
	vector<ProcEntry*>::iterator iter_p_proc_entry;
	for( iter_p_proc_entry = vp_proc_entries_.begin();
		iter_p_proc_entry != vp_proc_entries_.end();
		iter_p_proc_entry++ )
	{
		ss_result << ((*iter_p_proc_entry)->proc_idx) << " : " << ((*iter_p_proc_entry)->proc_name) <<
			" [" << ((*iter_p_proc_entry)->stmt_num_start) << ", " << ((*iter_p_proc_entry)->stmt_num_end) << "]"<< 
			" hierarchy : " << ((*iter_p_proc_entry)->proc_hierarchy) << "\n\t";

		ss_result << "CALLERS :";
		set<ProcIdx>::iterator iter_proc_idx_caller;
		for( iter_proc_idx_caller = (*iter_p_proc_entry)->set_callers.begin();
			iter_proc_idx_caller != (*iter_p_proc_entry)->set_callers.end();
			iter_proc_idx_caller++ )
		{
			ss_result << " " << GetProcEntry(*iter_proc_idx_caller)->proc_name;
		}

		ss_result << "\n\tCALLEES :";
		set<ProcIdx>::iterator iter_proc_idx_callee;
		for( iter_proc_idx_callee = (*iter_p_proc_entry)->set_callees.begin();
			iter_proc_idx_callee != (*iter_p_proc_entry)->set_callees.end();
			iter_proc_idx_callee++ )
		{
			ss_result << " " << GetProcEntry(*iter_proc_idx_callee)->proc_name;
		}

		ss_result << endl;
	}
	return ss_result.str();
}
void ProcTable::InitProcPathTable(){
	if( ProcPath_table_.empty() )
	{
		ProcPath_table_.resize(ProcTable::GetSize());
		for( int i = 0; i < ProcTable::GetSize(); i++ )
		{
			ProcPath_table_[i].resize(ProcTable::GetSize(), false);
		}
	}
}

void ProcTable::SetProcPath(ProcIdx proc_idx_previous, ProcIdx proc_idx_current)
{
	ProcPath_table_[proc_idx_previous - 1][proc_idx_current - 1] = true;
}

bool ProcTable::IsProcPath(ProcIdx proc_idx_1, ProcIdx proc_idx_2)
{
	return ProcPath_table_[proc_idx_1 - 1][proc_idx_2 - 1];
}

bool ProcTable::CheckProcNameExistence(ProcName proc_name, ProcEntry** pp_proc_entry)
{
	vector<ProcEntry*>::iterator iter_p_proc_entry;
	for( iter_p_proc_entry = vp_proc_entries_.begin();
		iter_p_proc_entry != vp_proc_entries_.end();
		iter_p_proc_entry++)
	{
		if( (*iter_p_proc_entry)->proc_name.compare(proc_name) == 0 )
		{
			*pp_proc_entry = *iter_p_proc_entry;
			return true;
		}
	}
	return false;
}

ProcEntry* ProcTable::GetProcEntry(ProcIdx proc_idx)
{
	return vp_proc_entries_[proc_idx-1];
}